package src

import (
	"fmt"
	"sync"
	"time"

	"github.com/go-redis/redis/v7"
	"github.com/gofrs/uuid/v5"
)

type RenewalTicker struct {
	T *time.Ticker
	C chan []string
	Q map[string]string
	R *redis.Client

	cnt     int
	closed  bool
	tclosed bool
	mutex   sync.Mutex
}

var (
	once sync.Once
	rt   *RenewalTicker

	duration       = 5 * time.Second
	rq_empty_count = 10
)

func GetRenewalTicker() *RenewalTicker {
	once.Do(func() {
		rt = &RenewalTicker{
			T: time.NewTicker(duration),
			Q: make(map[string]string),
			C: make(chan []string),
		}
		rt.newCli()
		go rt.ticker()
	})
	return rt
}

func (r *RenewalTicker) send(val []string) {
	if len(val) == 0 {
		return
	}
	// 避免 panic , 当通道意外关闭的时候写入
	defer func() {
		if err := recover(); err != nil {
			fmt.Println("send: ", err)
		}
	}()
	r.C <- val
}

func (r *RenewalTicker) cclosed() {
	// 避免 panic , 当通道意外关闭的时候写入
	defer func() {
		if err := recover(); err != nil {
			fmt.Println("cclosed: ", err)
		}
	}()
	close(r.C)
}

func (r *RenewalTicker) newCli() {
	r.R = redis.NewClient(&redis.Options{
		Addr:     "192.168.1.45:1379",
		Password: "123456",
	})
}

func (r *RenewalTicker) ExampleNewClient(key string) {
	uid, _ := uuid.NewV6()
	lockCmd := "if redis.call('setnx', KEYS[1], KEYS[2]) == 1 " +
		"then " +
		"   redis.call('expire', KEYS[1], ARGV[1]) " +
		"	return 1 " +
		"else " +
		"	return 0 " +
		"end"

	i, err := r.R.Eval(lockCmd, []string{key, uid.String()}, 10).Result()
	fmt.Println(uid.String(), i, err)
	if err == nil && i.(int64) == 1 {
		if r.closed {
			r.activecheck()
		}
		r.send([]string{key, uid.String()})
	}
}

func (r *RenewalTicker) ticker() {
	renewCmd := "if redis.call('get', KEYS[1]) == KEYS[2] " +
		"then " +
		"   redis.call('expire', KEYS[1], ARGV[1]) " +
		"	return 1 " +
		"elseif redis.call('exists', KEYS[1]) == 0 " +
		"then " +
		"	return 0 " +
		"else " +
		"	return 2 " +
		"end"
	var closed bool
	for {
		if closed && r.tclosed {
			r.stateclosed()
			fmt.Printf("closed: %v \n", r.closed)
			return
		}
		select {
		case val, ok := <-r.C:
			if !ok {
				closed = true
				fmt.Println("chan closed")
				break
			}
			if len(val) == 2 {
				r.add(true)
				r.Q[val[1]] = val[0]
			}
		case <-r.T.C:
			l := len(r.Q)
			r.add(false)
			for v, k := range r.Q {
				i, err := r.R.Eval(renewCmd, []string{k, v}, 10).Result()
				if i == 0 {
					delete(r.Q, v)
				}
				fmt.Println(l, k, v, i, err)
			}
		}
	}
}

func (r *RenewalTicker) add(reset bool) {
	if r.tclosed {
		return
	}

	r.mutex.Lock()
	defer r.mutex.Unlock()

	if reset {
		if r.tclosed {
			r.T.Reset(duration)
		}
	} else {
		r.cnt++
		if r.cnt > rq_empty_count {
			r.tickerclosed()
		}
	}
}

func (r *RenewalTicker) tickerclosed() {
	if !r.tclosed {
		r.tclosed = true
		r.T.Stop()
		fmt.Println("ticker closed")
	}
}

func (r *RenewalTicker) RtClosedChannel() {
	r.mutex.Lock()
	defer r.mutex.Unlock()

	r.cclosed()
	r.tickerclosed()
}

func (r *RenewalTicker) stateclosed() {
	r.mutex.Lock()
	defer r.mutex.Unlock()

	r.closed = true
	r.R.Close()
	r.Q = nil
}

func (r *RenewalTicker) activecheck() {
	r.mutex.Lock()
	defer r.mutex.Unlock()

	if !r.closed {
		return
	}
	r.closed = false
	r.tclosed = false
	r.C = make(chan []string)
	r.T.Reset(duration)

	r.newCli()
	go r.ticker()
}

func (r *RenewalTicker) DelCmd() {
	r.RtClosedChannel()
	delCmd := "if redis.call('get', KEYS[1]) == KEYS[2] " +
		"then " +
		"	redis.call('del', KEYS[1])" +
		"	return 1 " +
		"else " +
		"	return 0 " +
		"end"
	for v, k := range r.Q {
		i, err := r.R.Eval(delCmd, []string{k, v}).Result()
		if i == 1 && err == nil {
			delete(r.Q, v)
		}
		fmt.Println("del", k, v, i, err)
	}
}
